//
//  BGDBProcess.m
//  BTDatabaseAccesser
//
//  Created by XMac on 2018/4/23.
//  Copyright © 2018年 XMac. All rights reserved.
//

#import "BGDBProcess.h"
#import <FMDB/FMDB.h>
#import <YYModel/YYModel.h>
#import "BGDatabaseTableInfo.h"
#import "BGDatabaseColumnInfo.h"
@interface BGDBProcess()
@property (strong, nonatomic) FMDatabase *database;
@end

@implementation BGDBProcess

-(instancetype)initWithDB:(FMDatabase *)database{
    self = [super init];
    if (self) {
        self.database = database;
    }
    return self;
}



- (BOOL)insertObject:(id)obj {
    [self checkTable:object_getClass(obj)];
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:object_getClass(obj)];
    NSMutableArray *columNames = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    NSMutableArray *queries = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    NSMutableArray *values = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    for (int i = 0; i < tableInfo.columns.count; i++) {
        BGDatabaseColumnInfo *columnInfo = tableInfo.columns[i];
        id value = [obj valueForKeyPath:columnInfo.propertyName];
        if (!value || columnInfo.systemManaged) {
            continue;
        }
        [columNames addObject:columnInfo.columnName];
        [values addObject:value];
        [queries addObject:@"?"];
    }
    NSString *columnString = [columNames componentsJoinedByString:@","];
    NSString *queryString = [queries componentsJoinedByString:@","];
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES(%@)", tableInfo.tableName, columnString, queryString];
    BOOL executeResult = [self.database executeUpdate:sql withArgumentsInArray:values];
    return executeResult;
}

- (BOOL)deleteWithClass:(Class)clz condition:(NSString *)condition value:(va_list)args{
    [self checkTable:clz];
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:clz];
     NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", tableInfo.tableName, condition];
    BOOL rs = [self.database executeUpdate:sql withVAList:args];
    return rs;
}

- (BOOL)updateWithItem:(id)obj {

    return [self updateWithItem:obj conditions:nil];
}

- (BOOL)updateWithItem:(id)obj conditions:(NSString *)conditions, ...
{
    [self checkTable:object_getClass(obj)];
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:object_getClass(obj)];
    NSAssert(tableInfo.primaryKey, @"Must assign a primary key when condition is nil");
    NSMutableArray *updateValues = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    NSMutableArray *updateComponents = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    for (int i = 0; i < tableInfo.columns.count; i++) {
        BGDatabaseColumnInfo *columnInfo = tableInfo.columns[i];
        id value = [obj valueForKeyPath:columnInfo.propertyName];
        if (!value || columnInfo.systemManaged) {
            continue;
        }
        [updateValues addObject:value];
        [updateComponents addObject:[NSString stringWithFormat:@"%@=?", columnInfo.columnName]];
    }
    NSString *updateString = [updateComponents componentsJoinedByString:@","];
    if (!conditions.length) {
        id primaryKeyValue = [obj valueForKeyPath:tableInfo.primaryKey.propertyName];
        NSAssert(primaryKeyValue, @"Primary key value must not be nil when condition is nil");
        [updateValues addObject:primaryKeyValue];
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@=?", tableInfo.tableName, updateString, tableInfo.primaryKey.columnName];
        BOOL rs = [self.database executeUpdate:sql withArgumentsInArray:updateValues];
        return rs;
    } else {
        NSMutableArray *args = [NSMutableArray array];
        va_list argsList;
        va_start(argsList, conditions);
        id arg;
        while ((arg = va_arg(argsList, id))) {
            [args addObject:arg];
        }
        va_end(argsList);
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@", tableInfo.tableName, updateString, conditions];
        BOOL rs = [self.database executeUpdate:sql withArgumentsInArray:args];
        
        return rs;
    }
}

- (NSArray *)queryItemsWithClass:(Class)clz condition:(NSString *)condition value:(va_list)args {
    [self checkTable:clz];
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:clz];
    NSMutableArray *items = [NSMutableArray array];
    NSMutableArray *columnNames = [NSMutableArray arrayWithCapacity:tableInfo.columns.count];
    for (BGDatabaseColumnInfo *columnInfo in tableInfo.columns) {
        [columnNames addObject:columnInfo.columnName];
    }
    NSString *columnString = [columnNames componentsJoinedByString:@","];
    NSString *sql = [NSString stringWithFormat:@"SELECT %@ FROM %@", columnString, tableInfo.tableName];
    if (condition.length) {
        sql = [sql stringByAppendingFormat:@" %@",condition];
    }
    FMResultSet *resultSet = [self.database executeQuery:sql withVAList:args];
    while (resultSet.next) {
        [items addObject:[self itemFromResultSet:resultSet class:clz]];
    }
    [resultSet close];
    return items;
}

- (BOOL)deleteAllWithClass:(Class)clz {
    [self checkTable:clz];
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:clz];
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@", tableInfo.tableName];
    BOOL rs = [self.database executeUpdate:sql];
    return rs;
}


//todo execute的功能需要扩展1、回调  2、表是否存在的判断
- (BOOL)executeUpdate:(NSString *)sql {
    return [self.database executeUpdate:sql];
}

- (FMResultSet *)executeQuery:(NSString *)sql {
    return [self.database executeQuery:sql];
}


/**
 检查是否已经有这个表了,如果没有就新建一个表。

 @param clz .
 */
-(void)checkTable:(Class)clz{
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:clz];
    FMResultSet *rs = [self.database executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableInfo.tableName];
    NSInteger count = 0;
    while ([rs next])
    {
        count = [rs intForColumn:@"count"];
        break;
    }
    if (count == 0) {
        [self createTableForClass:clz];
    }
}

- (BOOL)createTableForClass:(Class)cls
{
    BGDatabaseTableInfo *databaseInfo = [BGDatabaseTableInfo tableInfoForClass:cls];
    NSMutableArray *columnComponents = [NSMutableArray arrayWithCapacity:databaseInfo.columns.count];
    for (BGDatabaseColumnInfo *column in databaseInfo.columns) {
        NSString *columnTypeDesc = BGDatabaseColumnTypeDesc(column.type);
        NSMutableString *columnString = [NSMutableString stringWithFormat:@"%@ %@", column.columnName, columnTypeDesc];
        BOOL shouldInsert = NO;
        if ([column isPrimaryKey]) {
            shouldInsert = YES;
            [columnString appendString:@" PRIMARY KEY"];
        }
        if (shouldInsert) {
            [columnComponents insertObject:columnString atIndex:0];
        } else {
            [columnComponents addObject:columnString];
        }
    }
    NSString *columnsString = [columnComponents componentsJoinedByString:@","];
    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@)", databaseInfo.tableName, columnsString];
    BOOL executeResult = [self.database executeUpdate:sql];
    return executeResult;
}

- (id)itemFromResultSet:(FMResultSet *)resultSet class:(Class)clz{
    BGDatabaseTableInfo *tableInfo = [BGDatabaseTableInfo tableInfoForClass:clz];
    id item = [[clz alloc] init];
    for (BGDatabaseColumnInfo *columnInfo in tableInfo.columns) {
        id value = nil;
        switch (columnInfo.type) {
            case BGDatabaseColunTypeText: {
                value = [resultSet stringForColumn:columnInfo.columnName];
                break;
            }
            case BGDatabaseColunTypeReal:
            case BGDatabaseColunTypeFloat:
            case BGDatabaseColunTypeDouble:
            case BGDatabaseColunTypeLongDouble: {
                float floatValue = [resultSet doubleForColumn:columnInfo.columnName];
                value = @(floatValue);
                break;
            }
            case  BGDatabaseColunTypeInteger: {
                long long intValue = [resultSet longLongIntForColumn:columnInfo.columnName];
                value = @(intValue);
                break;
            }
            case BGDatabaseColunTypeBoolean: {
                BOOL boleanValue = [resultSet boolForColumn:columnInfo.columnName];
                value = @(boleanValue);
                break;
            }
            case BGDatabaseColunTypeBlob: {
                NSData *dataValue = [resultSet dataForColumn:columnInfo.columnName];
                value = dataValue;
                break;
            }
            case BGDatabaseColunTypeDate: {
                NSDate *date = [resultSet dateForColumn:columnInfo.columnName];
                value = date;
                break;
            }
            default:
                break;
        }
        if ([item respondsToSelector:@selector(setValueForProperty:dabaseValue:)]) {
            [(id<BGDatabaseAccessible>)item setValueForProperty:columnInfo.propertyName dabaseValue:value];
        } else {
            [item setValue:value forKey:columnInfo.propertyName];
        }
    }
    return item;
}

@end
